/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.controller;

import java.awt.event.ActionEvent;

import javax.swing.JEditorPane;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.html.HTMLEditorKit.HTMLTextAction;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;

import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.model.Range;

public class UndoActions {

    static class CaretEdit extends AbstractUndoableEdit {

        Range range;
        JEditorPane pane;

        public CaretEdit(JEditorPane pane) {
            this.range = new Range(pane.getSelectionStart(), pane.getSelectionEnd());
            this.pane = pane;
        }

        public String getPresentationName() {
            return "CaretEdit";
        }

        public void redo() throws CannotRedoException {
            super.redo();
            if (pane != null) {
                pane.setSelectionStart(range.getStartOffset());
                pane.setSelectionEnd(range.getEndOffset());
            }
        }

        public void undo() throws CannotUndoException {
            super.undo();
            if (pane != null) {
                pane.setSelectionStart(range.getStartOffset());
                pane.setSelectionEnd(range.getEndOffset());
            }
        }
    }

    public static class EditorActionUndoManager extends UndoManager {

        CompoundEdit compoundEdit = null;
        Editor editor;

        public EditorActionUndoManager(Editor editor) {
            this.editor = editor;
        }

        public void startAction(JEditorPane pane) {
            compoundEdit = new CompoundEdit();
            if (pane != null)
                compoundEdit.addEdit(new CaretEdit(pane));
        }

        public void endAction(JEditorPane pane) {
            if (pane != null)
                compoundEdit.addEdit(new CaretEdit(pane));
            compoundEdit.end();

            super.addEdit(compoundEdit);
            compoundEdit = null;

            editor.getUndoAction().updateUndoState();
            editor.getRedoAction().updateRedoState();
        }

        public boolean addEdit(UndoableEdit edit) {
            if (compoundEdit == null)
                return super.addEdit(edit);

            return compoundEdit.addEdit(edit);
        }
    }

    /**
     * Class for implementing Undo as an autonomous action
     */
    public static class UndoAction extends HTMLTextAction {

        Editor editor;

        public UndoAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Undo"));
            putValue(ACCELERATOR_KEY, javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK));
            putValue(SMALL_ICON, new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_undo.png")));
            this.editor = editor;
        }

        public void actionPerformed(ActionEvent e) {
            try {
                editor.getUndoManager().undo();
            } catch (CannotUndoException ex) {
                ex.printStackTrace();
            }
            updateUndoState();
            editor.getRedoAction().updateRedoState();
        }

        public void updateUndoState() {
            boolean canUndo = editor.getUndoManager().canUndo();
            setEnabled(editor.getUndoManager().canUndo());
        }
    }

    /**
     * Class for implementing Redo as an autonomous action
     */
    public static class RedoAction extends HTMLTextAction {
        Editor editor;

        public RedoAction(Editor editor) {
            super(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("Redo"));
            setEnabled(false);
            putValue(ACCELERATOR_KEY, javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Y, java.awt.event.InputEvent.CTRL_MASK));
            putValue(SMALL_ICON, new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/editor/view/resources/sc_redo.png")));
            this.editor = editor;
        }

        public void actionPerformed(ActionEvent e) {
            try {
                editor.getUndoManager().redo();
            } catch (CannotUndoException ex) {
                ex.printStackTrace();
            }
            updateRedoState();
            editor.getUndoAction().updateUndoState();
        }

        public void updateRedoState() {
            setEnabled(editor.getUndoManager().canRedo());
        }
    }

    /**
     * Class for implementing the Undo listener to handle the Undo and Redo
     * actions
     */
    public static class EditorUndoableEditListener implements UndoableEditListener {
        Editor editor;

        public EditorUndoableEditListener(Editor editor) {
            editor.getUndoManager().discardAllEdits();
            editor.getUndoAction().updateUndoState();
            editor.getRedoAction().updateRedoState();
            this.editor = editor;
        }

        public void undoableEditHappened(UndoableEditEvent uee) {
            editor.getUndoManager().addEdit(uee.getEdit());
            editor.getUndoAction().updateUndoState();
            editor.getRedoAction().updateRedoState();
        }

        public void reset() {
            editor.getUndoManager().discardAllEdits();
            editor.getUndoAction().updateUndoState();
            editor.getRedoAction().updateRedoState();
        }
    }
}
